/* Copyright (c) 2008 LepomisLab.com, Andrey Turkin
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "NSKeyedUnarchiver.h"
#include "NSString.h"
#include "NSObject.h"

NSKeyedUnarchiver::NSKeyedUnarchiver(wxObject *archive)
{
	wxASSERT(archive);
	NSDictionary* dict = wxDynamicCast(archive, NSDictionary);
	wxASSERT(dict);

	wxObject *obj = dict->getObject(wxT("$objects"));
	objects = wxDynamicCast(obj, NSArray);
	obj = dict->getObject(wxT("$top"));
	keymap = wxDynamicCast(obj, NSDictionary);
	wxASSERT(objects);
	wxASSERT(keymap);
	objmap = new wxObject* [objects->length()];
	for(size_t i=0;i<objects->length();i++)
		objmap[i] = NULL;
}
NSKeyedUnarchiver::~NSKeyedUnarchiver()
{
	delete objmap;
}

wxObject *NSKeyedUnarchiver::decodeObject(long key)
{
	if (!objmap[key])
	{
		wxObject *obj = objects->getObject(key);
		wxASSERT(obj);
		NSDictionary *dict = wxDynamicCast(obj, NSDictionary);
		if (!dict)
		{
			wxASSERT(obj);
			objmap[key] = obj;
		} else {
			NSDictionary *d2 = wxDynamicCast(dict->getObject(wxT("$class")), NSDictionary);
			wxASSERT(d2);
			wxVariant *v = wxDynamicCast(d2->getObject(wxT("CF$UID")), wxVariant);
			wxASSERT(v);
			d2 = wxDynamicCast(objects->getObject(v->GetInteger()), NSDictionary);
			wxASSERT(d2);
			v = wxDynamicCast(d2->getObject(wxT("$classname")), wxVariant);
			wxASSERT(v);
			NSDictionary *old_keymap = keymap;
			keymap = dict;
			wxString className(v->GetString());

			obj = NSObject::createDynamicObject(className);
			objmap[key] = obj;
			NSObject *nso = wxDynamicCast(obj, NSObject);
			wxASSERT(nso);
			
			nso->initWithUnarchiver(this); /* must not use nso later on because it may be deleted in case of ClassSwapper */

			keymap = old_keymap;
		}
	}
	return objmap[key];
}
void NSKeyedUnarchiver::replaceObject(NSObject* oldObject, NSObject* newObject)
{
	for(size_t i = 0;i < objects->length(); i++)
		if (objmap[i] == oldObject)
			objmap[i] = newObject;
}

wxObject *NSKeyedUnarchiver::decodePlistForKey(const wxString &key)
{
	return keymap->getObject(key);
}

NSData* NSKeyedUnarchiver::decodeDataForKey(const wxString& key)
{
	NSData* o = wxDynamicCast(keymap->getObject(key), NSData);
	wxASSERT(o);
	return o;
}

wxObject *NSKeyedUnarchiver::decodeObjectForKey(const wxString &key)
{
	wxObject *o = keymap->getObject(key);
	if (!o)
		return NULL;
	NSDictionary *obj = wxDynamicCast(o, NSDictionary);
	wxASSERT(obj);
	wxVariant *v = wxDynamicCast(obj->getObject(wxT("CF$UID")), wxVariant);
	wxASSERT(v);
	wxObject *ret = decodeObject(v->GetInteger());
	return ret;
}

wxObject *NSKeyedUnarchiver::decodeArrayObjectForKey(const wxString &key)
{
	wxObject *o = keymap->getObject(key);
	NSArray *obj = wxDynamicCast(o, NSArray);
	if (!obj)
		return NULL;
	size_t len = obj->length();
	wxObject **arr = new wxObject *[len];
	for(size_t i = 0;i < len; i++)
	{
		NSDictionary *d = wxDynamicCast(obj->getObject(i), NSDictionary);
		wxASSERT(d);
		wxVariant *v = wxDynamicCast(d->getObject(wxT("CF$UID")), wxVariant);
		wxASSERT(v);
		arr[i] = decodeObject(v->GetInteger());
	}
	return new NSArray(arr, len);
}

long NSKeyedUnarchiver::decodeIntForKey(const wxString &key)
{
	wxObject *o = keymap->getObject(key);
	wxVariant *obj = wxDynamicCast(o, wxVariant);
	wxASSERT(obj);
	return obj->GetInteger();
}

double NSKeyedUnarchiver::decodeFloatForKey(const wxString &key)
{
	wxObject *o = keymap->getObject(key);
	wxVariant *obj = wxDynamicCast(o, wxVariant);
	wxASSERT(obj);
	return obj->GetDouble();
}

bool NSKeyedUnarchiver::decodeBoolForKey(const wxString &key)
{
	wxObject *o = keymap->getObject(key);
	wxVariant *obj = wxDynamicCast(o, wxVariant);
	wxASSERT(obj);
	return obj->GetBool();
}
wxString NSKeyedUnarchiver::decodeStringForKey(const wxString &key)
{
	wxObject *o = decodeObjectForKey(key);
	wxASSERT(o);
	wxVariant *v = wxDynamicCast(o, wxVariant);
	if (v)
		return wxString(v->GetString());
	NSMutableString* s = wxDynamicCast(o, NSMutableString);
	wxASSERT(s);
	return *s;
}

wxRect *NSKeyedUnarchiver::decodeRectForKey(const wxString &key)
{
	wxVariant *v = wxDynamicCast(decodeObjectForKey(key), wxVariant);
	wxASSERT(v);
	wxString str(v->GetString());
	wxString str2;
	if (!str.StartsWith(wxT("{{"), &str2) || !str2.EndsWith(wxT("}}"), &str))
		wxASSERT(false);
	size_t pos = str.find(','); wxASSERT(pos != wxString::npos);
	wxString x_s = str.substr(0, pos);
	pos++;
	size_t pos2 = str.find('}', pos); wxASSERT(pos2 != wxString::npos);
	wxString y_s = str.substr(pos, pos2-pos);
	double x, y;
	if (!x_s.ToDouble(&x) || !y_s.ToDouble(&y))
		wxASSERT(false);
	str.Remove(0, pos2+1);
	if (!str.StartsWith(wxT(", {"), &str2))
		wxASSERT(false);
	pos = str2.find(','); wxASSERT(pos != wxString::npos);
	x_s = str2.substr(0, pos);
	y_s = str2.substr(pos+1);
	double w, h;
	if (!x_s.ToDouble(&w) || !y_s.ToDouble(&h))
		wxASSERT(false);
	return new wxRect(x, y, w, h);
}

wxSize *NSKeyedUnarchiver::decodeSizeForKey(const wxString &key)
{
	wxVariant *v = wxDynamicCast(decodeObjectForKey(key), wxVariant);
	wxASSERT(v);
	wxString str(v->GetString());
	if (!str.StartsWith(wxT("{")) || !str.EndsWith(wxT("}")))
	{
		wxASSERT(false);
	}
	str.Remove(0, 1).RemoveLast();
	size_t pos = str.find(','); wxASSERT(pos != wxString::npos);
	wxString x_s = str.substr(0, pos);
	str.Remove(0, pos+1);
	double x, y;
	if (!x_s.ToDouble(&x) || !str.ToDouble(&y))
		wxASSERT(false);
	return new wxSize(x, y);
}

bool NSKeyedUnarchiver::contains(const wxString &key)
{
	return keymap->getObject(key) != NULL;
}
